// 函数声明
// 一个函数有输入和输出，要在 TypeScript 中对其进行约束，需要把输入和输出都考虑到，其中函数声明的类型定义较简单：
// function sum(x: number, y: number): number {
//   return x + y;
// }
// 注意，输入多余的（或者少于要求的）参数，是不被允许的
// sum(1); // error
// sum(1, 2, 3) // error

// 函数表达式
let mySum = function (x: number, y: number): number {
    return x + y;
};
// 这是可以通过编译的，不过事实上，上面的代码只对等号右侧的匿名函数进行了类型定义，而等号左边的 mySum，是通过赋值操作进行类型推论而推断出来的。如果需要我们手动给 mySum 添加类型，则应该是这样：
let mySum1: (x: number, y: number) => number = (x: number, y: number): number =>
    x + y;

// 用接口定义函数的形状
interface SearchFunc {
    (firstName: string, lastName: string): boolean;
}
let mySearchFunc: SearchFunc;
mySearchFunc = function (source: string, subString: string) {
    return source.search(subString) !== -1;
};
// 采用函数表达式|接口定义函数的方式时，对等号左侧进行类型限制，可以保证以后对函数名赋值时保证参数个数、参数类型、返回值类型不变。

// 可选参数
// function buildName(firstName: string, lastName?: string) {
//   if (lastName) {
//     return firstName + ' ' + lastName;
//   } else {
//     return firstName;
//   }
// }
// let tomcat = buildName('Tom', 'Cat')
// let tom = buildName('Tom')
// 需要注意的是，可选参数必须接在必需参数后面。换句话说，可选参数后面不允许再出现必需参数了：

// 参数默认值
function buildName(firstName: string, lastName: string = 'Cat') {
    return firstName + ' ' + lastName;
}
let tomcat = buildName('Tom', 'Cat');
let tom = buildName('Tom');
// 此时就不受「可选参数必须接在必需参数后面」的限制了：

// 剩余参数
// ES6 中，可以使用 ...rest 的方式获取函数中的剩余参数（rest 参数）：
function push(array, ...items: number[]) {
    items.forEach(function (item) {
        array.push(item);
    });
}
let a: any[] = [];
push(a, 1, 2, 3, 4, 5);
console.log(a);

// 重载
// 重载允许一个函数接受不同数量或类型的参数时，作出不同的处理。
// 比如，我们需要实现一个函数 reverse，输入数字 123 的时候，输出反转的数字 321，输入字符串 'hello' 的时候，输出反转的字符串 'olleh'。
// 利用联合类型
function reverse(x: number | string): number | string | void {
    if (typeof x === 'number') {
        return Number(x.toString().split('').reverse().join(''));
    } else if (typeof x === 'string') {
        return x.split('').reverse().join('');
    }
}
console.log(reverse(123456));
console.log(reverse('123456'));
